<?php

namespace app\admin\controller\fastflow\flow;

use app\admin\model\AuthRule;
use app\admin\model\fastflow\Carbon;
use app\admin\model\fastflow\flow\BillAuth;
use app\common\controller\Backend;
use think\Config;
use think\console\Input;
use think\Db;
use think\Exception;
use think\Loader;
use fastflow\api;
use think\response\Json;
use think\Session;

/**
 * 单据管理
 *
 * @icon fa fa-clipboard
 */
class Bill extends Backend
{

    /**
     * Bill模型对象
     * @var \app\admin\model\fastflow\flow\Bill
     */
    protected $model = null;
    protected $noNeedRight = ['getBadge', 'getFieldsWithComment', 'getTablesAjax'];

    public function _initialize()
    {
        parent::_initialize();
        $this->model = new \app\admin\model\fastflow\flow\Bill;

    }

    private function getTables()
    {
        $tables = Db::query("select TABLE_NAME as name,TABLE_COMMENT as comment from information_schema.tables where table_schema='" . Config::get('database')['database'] . "'");
        return $tables;
    }

    public function getTablesAjax()
    {
        $tables = $this->getTables();
        return ['code' => 1, 'msg' => '获取成功', 'data' => $tables];
    }

    /**
     * 从数据表创建
     */
    public function fromtable()
    {
        if ($this->request->isGet()) {
            $tables = $this->getTables();
            $this->view->assign("tables", $tables);
            return $this->view->fetch();
        }
        elseif ($this->request->isPost()) {
            $params = input('');
            $crud_result = $this->crud($params);
            if ($crud_result['code'] == 0) {
                return $crud_result;
            }
            return ['code' => 1, 'msg' => '单据创建成功'];
        }
    }


    /**
     * 编辑
     */
    public function edit($ids = null)
    {
        if ($this->request->isPost()) {
            return ['code' => 0, 'msg' => '不允许直接对该表进行编辑'];
        }
        return $this->view->fetch('cannotedit');
    }

    /**
     * 删除
     */
    public function del($ids = "")
    {
        $bill = $this->model->find($ids);
        if ($bill) {
            $params = [];
            $params['commandtype'] = 'crud';
            $params['delete'] = 1;
            $params['menu'] = 1;
            $params['table'] = $bill['bill_table'];
            $params['controller'] = $bill['controller'];

            if (!$this->command($params, 'execute')) {
                return ['code' => 0, 'msg' => '删除单据失败'];
            }
            unset($params['controller']);
            $params['force'] = 1;
            $params['commandtype'] = 'menu';
            $params['controllerfile'] = $bill['controller'];
            $this->command($params, 'execute');
            $bill->delete();
            if (count($this->model->select()) == 0) {
                $bill_rule = (new AuthRule())->where('name', 'fastflow/bill')->find();
                if ($bill_rule) {
                    $bill_rule->status = 'hidden';
                    $bill_rule->save();
                }
            }
            return ['code' => 1, 'msg' => '删除单据成功'];
        }
        else {
            return ['code' => 0, 'msg' => '删除单据失败'];
        }
    }

    public function getFieldsWithComment()
    {
        $table = input('table');
        $fileds = (new api())->getFieldsWithComment($table);
        return ['code' => 1, 'msg' => '获取成功', 'data' => $fileds];
    }


    private function command($params, $action = '')
    {
        $commandtype = $params['commandtype'];
        $allowfields = [
            'crud' => 'table,controller,model,fields,force,local,delete,menu',
            'menu' => 'controller,delete,icon',
        ];
        $argv = [];
        $allowfields = isset($allowfields[$commandtype]) ? explode(',', $allowfields[$commandtype]) : [];
        $allowfields = array_filter(array_intersect_key($params, array_flip($allowfields)));
        if (isset($params['local']) && !$params['local']) {
            $allowfields['local'] = $params['local'];
        }
        else {
            unset($allowfields['local']);
        }
        foreach ($allowfields as $key => $param) {
            $argv[] = "--{$key}=" . (is_array($param) ? implode(',', $param) : $param);
        }
        if ($commandtype == 'crud') {
            if (isset($params['isrelation'])) {
                $isrelation = $params['isrelation'];
                if ($isrelation && isset($params['relation'])) {
                    foreach ($params['relation'] as $index => $relation) {
                        foreach ($relation as $key => $value) {
                            $argv[] = "--{$key}=" . (is_array($value) ? implode(',', $value) : $value);
                        }
                    }
                }
            }
        }
        elseif ($commandtype == 'menu') {
            foreach (explode(',', $params['controllerfile']) as $index => $param) {
                if ($param) {
                    $argv[] = "--controller=" . $param;
                }
            }
        }
        if ($action == 'execute') {
            return $this->doexecute($commandtype, $argv);
        }
        return;
    }

    private function doexecute($commandtype, $argv)
    {
        $commandName = "\\addons\\fastflow\\command\\" . ucfirst($commandtype);
        $input = new Input($argv);
        $output = new \addons\fastflow\command\Output();
        $command = new $commandName($commandtype);
        try {
            $command->run($input, $output);
            return true;
        } catch (Exception $e) {
            return false;
        }
    }


    public function getBadge()
    {
        $colors = ['top' => 'bg-red', 'second' => 'bg-orange', 'bill' => 'bg-red'];
        $shapes = ['top' => 'label', 'second' => 'badge', 'bill' => 'badge'];
        $auth_rule = new AuthRule();
        $bills = $this->model->select();
        $temp_arr = [];
        foreach ($bills as $bill) {
            $count = (new api())->getBillCanCheckThreadCount($bill['bill_table']);
            $level_arr = explode('/', $bill['controller']);
            $controllerName = Loader::parseName(end($level_arr), 0);
            if ($controllerName[0] == '_') {
                $controllerName[0] = '';
            }
            array_pop($level_arr);
            array_push($level_arr, $controllerName);
            for ($i = 0; $i < count($level_arr); $i++) {
                $level_str = implode('/', array_slice($level_arr, 0, $i + 1));
                $in_array = false;
                $index = 0;
                for ($j = 0; $j < count($temp_arr); $j++) {
                    if ($temp_arr[$j]['level'] == $level_str) {
                        $in_array = true;
                        $index = $j;
                        break;
                    }
                }

                if ($in_array) {
                    $temp_arr[$index]['count'] += $count;
                }
                else {
                    $temp_arr[] = ['level' => $level_str, 'count' => $count];
                }
            }
        }
        $data = [];
        $carbon_count = Carbon::where(['receiver_id' => Session::get()['admin']['id'], 'is_read' => 0])->count();
        $temp_arr[] = ['level' => 'fastflow/carbon', 'count' => $carbon_count];
        foreach ($temp_arr as $item) {
            $rule = $auth_rule->where(['name' => $item['level']])->find();
            if ($rule) {
                $menu_id = $rule['id'];
                if ($menu_id) {
                    if ($item['level'] == 'fastflow') {
                        $data[] = ['id' => $menu_id, 'count' => ($item['count'] + $carbon_count), 'type' => 'top', 'color' => $colors['top'], 'shape' => $shapes['top'], 'show' => ($item['count'] + $carbon_count)];
                    }
                    elseif ($item['level'] == 'fastflow/bill') {
                        $data[] = ['id' => $menu_id, 'count' => $item['count'], 'type' => 'second', 'color' => $colors['second'], 'shape' => $shapes['second'], 'show' => '待办'];
                    }
                    else {
                        $data[] = ['id' => $menu_id, 'count' => $item['count'], 'type' => 'bill', 'color' => $colors['bill'], 'shape' => $shapes['bill'], 'show' => $item['count']];
                    }
                }
            }
        }
        return ['code' => 1, 'msg' => '获取菜单角标数据成功', 'data' => $data];
    }

    public function auth($bill)
    {
        if ($this->request->isGet()) {
            if (!$bill) {
                $this->error('不能直接访问该页面', '');
            }
            else {
                $bill_row = $this->model->where(['bill_table' => $bill])->find();
                if (!$bill_row) {
                    $this->error('参数错误，未找到该单据', '');
                }
                $auth_row = (new BillAuth())->where(['bill' => $bill_row['bill_table']])->find();
                $view_rule = 1;
                $delete_rule = 1;
                if ($auth_row) {
                    $rules = json_decode($auth_row['rules'], true);
                    if (isset($rules['view'])) {
                        $view_rule = $rules['view'];
                    }
                    if (isset($rules['delete'])) {
                        $delete_rule = $rules['delete'];
                    }
                }
                $this->view->assign(['viewRule' => $view_rule, 'deleteRule' => $delete_rule]);
                return $this->view->fetch();
            }
        }
        elseif ($this->request->isPost()) {
            if (input('bill') == null || input('view') == null || input('delete') == null) {
                return ['code' => 0, 'msg' => '参数错误，请联系管理员'];
            }

            $bill = input('bill');
            $view_rule = input('view');
            $delete_rule = input('delete');
            $rules = [
                'view' => $view_rule,
                'delete' => $delete_rule
            ];
            $rules = json_encode($rules, JSON_NUMERIC_CHECK);
            $billAuthModel = new BillAuth();
            $auth_row = $billAuthModel->where(['bill' => $bill])->find();
            if (!$auth_row) {
                $billAuthModel->save(['bill' => $bill, 'rules' => $rules]);
            }
            else {
                $auth_row['rules'] = $rules;
                $auth_row->save();
            }
            return ['code' => 1, 'msg' => '单据权限配置成功'];
        }
    }

    /**
     * 新建
     */
    public function add()
    {
        if ($this->request->isGet()) {
            $prefix = Config::get('database')['prefix'];
            $this->assignconfig(['prefix' => $prefix]);
            return $this->view->fetch();
        }
        elseif ($this->request->isPost()) {
            $input = \input('');
            if (!isset($input['billTable']) || $input['billTable'] == '') {
                return ['code' => 0, 'msg' => '数据表名不能为空'];
            }
            if (!isset($input['formData'])) {
                return ['code' => 0, 'msg' => '表单不能为空'];
            }

            //检查表名是否包含前缀
            $table = $input['billTable'];
            $table = str_replace(' ', '', $table);
            $table = trim(trim($table), '_');
            $prefix = Config::get('database')['prefix'];
            $preg_result = preg_match('/^' . $prefix . '/', $table);
            if ($preg_result == 0) {
                $table = $prefix . $table;
            }
            $input['billTable'] = $table;

            $formData = $input['formData'];
            $check = $this->checkFromData($formData);
            if ($check['code'] == 0) {
                return $check;
            }

            $createResult = $this->createTable($input);
            if ($createResult['code'] == 0) {
                return $createResult;
            }

            $configData = $input['configData'];
            $params = [
                'fields' => [],
                'icon' => '',
                'controller' => '',
                'table' => $input['billTable'],
            ];
            foreach ($configData as $item) {
                if ($item['name'] == 'fields[]') {
                    $params['fields'][] = $item['value'];
                }
                if ($item['name'] == 'icon' || $item['name'] == 'controller') {
                    $params[$item['name']] = $item['value'];
                }
                if ($item['name'] == 'relation_json' && $item['value'] != '') {
                    $params['relation'] = json_decode($item['value'], true);
                }
            }
            if (isset($params['relation']) && isset($input['relationFields'][0]) && $input['relationFields'][0] != '') {
                $relation_fields = $input['relationFields'];
                foreach ($params['relation'] as $key => $item) {
                    $item_fields = $relation_fields[$key];
                    if (is_array($item_fields)) {
                        foreach ($item_fields as $field) {
                            $params['relation'][$key]['relationfields'][] = $field;
                        }
                    }
                    else {
                        $params['relation'][$key]['relationfields'] = '';
                    }
                }
            }
            $crud_result = $this->crud($params);
            if ($crud_result['code'] == 0) {
                return $crud_result;
            }
            return ['code' => 1, 'msg' => '单据创建成功'];
        }

    }

    private function crud($params)
    {
        $params['commandtype'] = 'crud';
        $must_fields = ['initiator_id', 'status'];
        $table = $params['table'];
        if ($this->model->where(['bill_table' => $table])->find()) {
            return ['code' => 0, 'msg' => $table . '表已存在对应单据，不能重复创建'];
        }
        $table_fields_width_comment = (new api())->getFieldsWithComment($table);
        $table_fields = [];
        foreach ($table_fields_width_comment as $fc) {
            $table_fields[] = $fc['field'];
        }
        foreach ($must_fields as $must_field) {
            if (!in_array($must_field, $table_fields)) {
                return ['code' => 0, 'msg' => '数据表字段必须包含' . $must_field . '字段'];
            }
        }

        $params['isrelation'] = 0;
        if (isset($params['relation'])) {
            $params['isrelation'] = 1;
        }
        $params['local'] = 1;
        $params['delete'] = 0;
        $params['force'] = 0;
        $prefix = Config::get('database')['prefix'];
        if ($params['controller'] == '') {
            $params['controller'] = 'fastflow/bill/' . Loader::parseName(str_replace($prefix, '', $params['table']), 1);
        }
        if (!$this->command($params, 'execute')) {
            return ['code' => 0, 'msg' => '执行错误，请检查是否已经存在对应控制器'];
        }
        $menu_params = [
            'commandtype' => 'menu',
            'allcontroller' => 0,
            'delete' => 0,
            'force' => 0,
            'controllerfile_text' => '',
            'controllerfile' => $params['controller'],
            'icon' => $params['icon'],
        ];
        if (!$this->command($menu_params, 'execute')) {
            return ['code' => 0, 'msg' => '生成菜单错误'];
        }
        $bill_rule = (new AuthRule())->where('name', 'fastflow/bill')->find();
        if ($bill_rule) {
            $bill_rule->ismenu = 1;
            $bill_rule->status = 'normal';
            $bill_rule->save();
        }
        $tables_comments = Db::query("select TABLE_NAME as name,TABLE_COMMENT as title from information_schema.tables where table_schema='" . Config::get('database')['database'] . "'");
        $bill_name = $params['table'];
        for ($i = 0; $i < count($tables_comments); $i++) {
            if ($tables_comments[$i]['name'] == $params['table']) {
                $bill_name = $tables_comments[$i]['title'];
                break;
            }
        }
        $this->model->save(['bill_name' => $bill_name, 'bill_table' => $params['table'], 'controller' => $params['controller'], 'params' => json_encode($params), 'createtime' => time(), 'updatetime' => time()]);
        return ['code' => 1, 'msg' => '新建单据成功'];
    }

    private function checkFromData($formData)
    {
        $common_must_form = [
            'name' => '字段名',
        ];
        $must_form = [
            'radio' => [
                'options' => '选项'
            ],
            'checkbox' => [
                'options' => '选项'
            ],
            'selectpicker' => [
                'options' => '选项'
            ],
        ];
        $widgets = $formData['widget'];
        foreach ($widgets as $widget) {
            foreach ($common_must_form as $key => $common) {
                if (!isset($widget[$key]) || $widget[$key] == '') {
                    return ['code' => 0, 'msg' => isset($widget['title']) && $widget['title'] != '' ? $widget['title'] . $common . '不能为空' : $common . '不能为空'];
                }
            }
            if (in_array($widget['type'], array_keys($must_form))) {
                foreach ($must_form[$widget['type']] as $key => $must) {
                    if (!isset($widget[$key]) || $widget[$key] == '') {
                        return ['code' => 0, 'msg' => isset($widget['title']) && $widget['title'] != '' ? $widget['title'] . $must . '不能为空' : $must . '不能为空'];
                    }
                }
            }
            //检验字段名是否以数字开头
            $patt = '/^[0-9]/';
            if (preg_match($patt, $widget['name']) != 0) {
                return ['code' => 0, 'msg' => isset($widget['title']) && $widget['title'] != '' ? $widget['title'] . '字段名不能以数字开头' : $widget['name'] . '字段名不能以数字开头'];
            }
        }
        return ['code' => 1];
    }


    private function createTable($params)
    {
        //表名
        $table = $params['billTable'];
        //注释
        $comment = $params['billName'];
        //是否包含创建时间字段
        $createtime = $params['createtime'];
        //是否包含更新时间字段
        $updatetime = $params['updatetime'];
        //表单数据
        $formData = $params['formData'];

        //检验表名是否以数字开头
        $prefix = Config::get('database')['prefix'];
        $patt = '/^[0-9]/';
        if (preg_match($patt, ltrim($table, $prefix)) != 0) {
            return ['code' => 0, 'msg' => '数据表名不能以数字开头'];
        }

        $tables = $this->getTables();
        for ($i = 0; $i < count($tables); $i++) {
            if ($tables[$i]['name'] == $table) {
                return ['code' => 0, 'msg' => '该表已存在，请重新命名'];
            }
        }


        $column_id = "`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',";
        $column_initiator_id = "`initiator_id` int(11) NULL COMMENT '发起人ID',";
        $column_status = "`status` enum('0','1','2','3') CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '状态:0=待发起,1=流程中,2=通过,3=终止',";
        $column_primary = "PRIMARY KEY (`id`) USING BTREE";

        $columns_sql = '';

        $sort = $formData['sort'];
        $widget = $formData['widget'];
        array_map(function ($widget_id) use ($widget, &$columns_sql) {
            $current_widget = null;
            for ($i = 0; $i < count($widget); $i++) {
                if ($widget_id == $widget[$i]['id']) {
                    $current_widget = $widget[$i];
                    break;
                }
            }
            if ($current_widget != null) {
                $columns_sql .= $this->createColumn($current_widget);
            }
        }, $sort);
        if ($createtime == 'true') {
            $columns_sql .= "`createtime` int(11) NULL DEFAULT NULL COMMENT '创建时间',";
        }
        if ($updatetime == 'true') {
            $columns_sql .= "`updatetime` int(11) NULL DEFAULT NULL COMMENT '更新时间',";
        }

        $sql = "CREATE TABLE `{$table}` ({$column_id}{$column_initiator_id}{$column_status}{$columns_sql}{$column_primary}) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '{$comment}';";
        Db::execute($sql);
        return ['code' => 1];
    }

    private function createColumn($widget)
    {
        $sql = '';
        $type = $widget['type'];
        $name = $widget['name'];
        $title = $widget['title'];
        $defaultValue = $widget['defaultValue'] == '' ? NULL : $widget['defaultValue'];
        if (!isset($widget['multiple'])) {
            $widget['multiple'] = '1';
        }
        $multiple = (int)$widget['multiple'];

        $suffix = [
            'selectpage' => [1 => '_id', 2 => '_ids'],
            'file' => [1 => 'file', 2 => 'files'],
            'image' => [1 => 'image', 2 => 'images'],
            'datetimeRange' => [1 => 'range'],
            'switch' => [1 => 'switch'],
            'radio' => [1 => 'data'],
            'checkbox' => [1 => 'data'],
            'tagsinput' => [1 => 'tag'],
            'fieldlist' => [1 => 'json'],
        ];

        //检查默认值是否在选项内，没在选项内则设为第一个选项
        $need_check_default = ['radio', 'checkbox', 'selectpicker'];
        if (in_array($type, $need_check_default)) {
            $is_contain = false;
            foreach ($widget['options'] as $option) {
                if ($option['key'] == $defaultValue){
                    $is_contain = true;
                }
            }
            if (!$is_contain){
                $defaultValue = $widget['options'][0]['key'];
            }
        }

        $suffix_keys = array_keys($suffix);
        if (in_array($type, $suffix_keys)) {
            $field_suffix = $suffix[$type][$multiple];
            $patt = "/{$field_suffix}$/";
            $name = preg_match($patt, $widget['name']) == 0 ? $name . $field_suffix : $name;
        }

        switch ($widget['type']) {
            case 'inputText':
                $sql = "`{$name}` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '{$defaultValue}' COMMENT '{$title}',";
                break;
            case 'textarea':
                $sql = "`{$name}` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '{$title}',";
                break;
            case 'inputNumber':
                $decimal = (int)$widget['decimal'] > 0 ? (int)$widget['decimal'] : 0;
                $field_type = $decimal > 0 ? 'float' : 'int';
                $defaultValue = $decimal > 0 ? round($defaultValue, $decimal) : (int)$defaultValue;
                $length = $decimal > 0 ? "(11,{$decimal})" : "(11)";
                $sql = "`{$name}` {$field_type}{$length} NULL DEFAULT '{$defaultValue}' COMMENT '{$title}',";
                break;
            case 'date':
                $sql = "`{$name}` date NULL COMMENT '{$title}',";
                break;
            case 'datetime':
                $sql = "`{$name}` datetime NULL COMMENT '{$title}',";
                break;
            case 'datetimeRange':
            case 'fieldlist':
                $sql = "`{$name}` varchar(255) NULL COMMENT '{$title}',";
                break;
            case 'radio':
                $keys = $this->parseKeys($widget['options']);
                $options = $this->parseOptions($widget['options']);
                $sql = "`{$name}` enum({$keys}) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '{$defaultValue}' COMMENT '{$title}:{$options}',";
                break;
            case 'checkbox':
                $keys = $this->parseKeys($widget['options']);
                $options = $this->parseOptions($widget['options']);
                $sql = "`{$name}` set({$keys}) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '{$defaultValue}' COMMENT '{$title}:{$options}',";
                break;
            case 'selectpicker':
                $keys = $this->parseKeys($widget['options']);
                $options = $this->parseOptions($widget['options']);
                $field_type = $multiple == 1 ? 'enum' : 'set';
                $sql = "`{$name}` {$field_type}({$keys}) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '{$defaultValue}' COMMENT '{$title}:{$options}',";
                break;
            case 'categorySelectpage':
            case 'userSelectpage':
            case 'adminSelectpage':
            case 'selectpage':
                $field_type = $multiple == 1 ? 'int(11)' : 'varchar(255)';
                $sql = "`{$name}` {$field_type} NULL COMMENT '{$title}',";
                break;
            case 'image':
            case 'tagsinput':
            case 'file':
                $sql = "`{$name}` varchar(255) NULL DEFAULT '{$defaultValue}' COMMENT '{$title}',";
                break;
            case 'switch':
                if ($defaultValue == '' || $defaultValue != '0' || $defaultValue != '1') {
                    $defaultValue = '1';
                }
                $sql = "`{$name}` tinyint(1) NOT NULL DEFAULT '{$defaultValue}' COMMENT '{$title}',";
                break;
        }
        return $sql;
    }

    private function parseOptions($options)
    {
        $optionsStr = '';
        foreach ($options as $item) {
            $optionsStr .= "{$item['key']}={$item['value']},";
        }
        return trim($optionsStr, ',');
    }

    private function parseKeys($options)
    {
        $keysStr = '';
        foreach ($options as $item) {
            $keysStr .= "'{$item['key']}',";
        }
        return trim($keysStr, ',');
    }

}
